package com.example.common.utils.sdk;

import com.example.common.utils.HttpClientUtil;
import com.example.common.utils.MD5;
import com.example.common.utils.XmlUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.dom4j.DocumentException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @Author: hwz
 * @Date: 2018/8/15
 */
@Slf4j
public class WxpayUtil {

    public static final String appSecrect = "047be7302e98748717ddfed952a49b33";
    public static final String app_id = "wx08ed0cfa29ea024f";
    public static final String mch_id = "1522635441";  // 商户号  1512845111
    public static final String key = "20190109154516CIEIHCJHHCEBHHHDDF";     // 商户平台设置的密钥key
    public static final String unified_order_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * 退款地址
     */
    public static final String refund_order_url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    public static final String trade_type = "APP";

    public static final String get_user_info = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

    /**
     * 证书地址
     */
    public static final String LOCAL_SSL = "C:"+File.separator +"websort"+File.separator+"apiclient_cert.p12";
    //public static final String LOCAL_SSL = "E:"+File.separator +"apiclient_cert.p12";
    public static final String ONLINE_SSL = "/home/projects/medium/files/wxpay/apiclient_cert.p12";

    public static Map<String, String> pay(HttpServletRequest servletRequest
                                            , String body, String detail, String out_trade_no, Double total_fee) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", app_id);
        params.put("mch_id", mch_id);
        params.put("nonce_str", getNonceStr());
        params.put("body", body);
        params.put("detail", detail);
        params.put("out_trade_no", out_trade_no);
        // 金额为分
        BigDecimal amount = BigDecimal.valueOf(total_fee);
        DecimalFormat format = new DecimalFormat("0");
        params.put("total_fee", format.format(amount.movePointRight(2)) + "");
        params.put("spbill_create_ip", /*IpUtil.getIpAddr(servletRequest)*/"47.110.52.118");
        params.put("notify_url", "http://xiangcheng.fanyin1036.com/webapi/"+ "mall/app/pay/data/wxNotify");
        params.put("trade_type", trade_type);
        // 签名
        sign(params, key);
        Map<String, Object> resultMap = getResult(servletRequest, unified_order_url, params, false, null);
        String returnCode = (String) resultMap.get("return_code");
        String resultCode = (String) resultMap.get("result_code");
        String prepayId = "";
        if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode))
            prepayId = (String) resultMap.get("prepay_id");
        if (StringUtils.isBlank(prepayId))
            return null;
        Map<String, String> payParams = buildPay(prepayId, params);
        sign(payParams, key);
        return payParams;
    }
    public static Map<String, Object> payKFPT(HttpServletRequest servletRequest
                                            , String body, String detail, String out_trade_no, Double total_fee, String localIP) {
        //Map<String, String> params = new HashMap<String, String>();
        //params.put("appid", app_id);
        //params.put("mch_id", mch_id);
        //params.put("nonce_str", getNonceStr());
        //params.put("body", body);
        //params.put("detail", detail);
        //params.put("out_trade_no", out_trade_no);
        //// 金额为分
        ////TODO 发布线上需要用金额
        ////BigDecimal amount = BigDecimal.valueOf(0.01);
        //BigDecimal amount = BigDecimal.valueOf(total_fee);
        //DecimalFormat format = new DecimalFormat("0");
        //params.put("total_fee", format.format(amount.movePointRight(2)) + "");
        ////设置服务器机器地址 不是用户地址!
        //params.put("spbill_create_ip",localIP);
        ////线上回调地址
        //params.put("notify_url",/*Constants.URL + */"/api/wxNotify");
        ////花生壳回掉地址
        ////params.put("notify_url","http://198s2a4752.iask.in"+ "/api/wxNotify");
        ////生成二维码输出 NATIVE
        //params.put("trade_type", "NATIVE");
        //// 签名
        //sign(params, key);
        //Map<String, Object> resultMap = getResult(servletRequest, unified_order_url, params, false, null);
        //String returnCode = (String) resultMap.get("return_code");
        //String resultCode = (String) resultMap.get("result_code");
        //if ("SUCCESS".equals(returnCode) && "SUCCESS".equals(resultCode)) {
        //    return resultMap;
        //} else {
        //    log.error("err_code:{}",resultMap.get("err_code"));
        //    log.error("err_code_des:{}",  resultMap.get("err_code_des"));
        //}
        return null;
    }

    /**
     * 测试微信扫码支付查询订单
     * @param servletRequest
     * @param out_trade_no 订单号
     * @return
     */
    public static Map<String, Object> searchWXKFPTOrderMsg(HttpServletRequest servletRequest, String out_trade_no){
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", app_id);
        params.put("mch_id", mch_id);
        params.put("out_trade_no", out_trade_no);
        params.put("nonce_str", getNonceStr());
        sign(params, key);
        Map<String, Object> resultMap = getResult(servletRequest, "https://api.mch.weixin.qq.com/pay/orderquery", params, false, null);
        return resultMap;
    }

    /**
     * 微信退款
     */
    public static String refuseAcount(HttpServletRequest servletRequest, String orderNum, Double account, Double amount){
        Map<String, String> params = new HashMap<String, String>();

        params.put("appid", app_id);
        params.put("mch_id", mch_id);
        params.put("nonce_str", getNonceStr());
        params.put("out_trade_no", orderNum);
        params.put("out_refund_no", System.currentTimeMillis()+"");
        // 金额为分
        BigDecimal preAcount = BigDecimal.valueOf(amount);
        BigDecimal refuseAcount = BigDecimal.valueOf(account);
        DecimalFormat format = new DecimalFormat("0");
        params.put("total_fee",format.format(preAcount.movePointRight(2)) + "");
        params.put("refund_fee", format.format(refuseAcount.movePointRight(2)) + "");
        // 签名
        sign(params, key);
        Map<String, Object> resultMap = getResult(servletRequest, refund_order_url, params, true, ONLINE_SSL);
        for(String key:resultMap.keySet())
        {
            System.out.println("Key: "+key+" Value: "+resultMap.get(key));
            log.info("Key: "+key+" Value: "+resultMap.get(key));
        }
        String result_code = (String) resultMap.get("result_code");

        return result_code;
    }

    public static void notify(HttpServletRequest req, HttpServletResponse resp) {
        try {
            req.setCharacterEncoding("utf-8");
            resp.setCharacterEncoding("utf-8");
            resp.setHeader("Content-type", "text/html;charset=UTF-8");
            Map<String, String> params = new HashMap<String, String>();
            String resString = XmlUtils.parseRequst(req);
            System.out.println("通知内容：" + resString);
            params.put("return_code", "fail");
            if (resString != null && !"".equals(resString)) {
                Map<String, String> reqParams = XmlUtils.toMap(
                        resString.getBytes(), "utf-8");
                if (!checkParam(reqParams, key))
                    params.put("return_msg", "签名失败");
                String out_trade_no = reqParams.get("out_trade_no");
                BigDecimal amount = BigDecimal.valueOf(0.01);
                DecimalFormat format = new DecimalFormat("0");
                String money = format.format(amount.movePointRight(2)) + "";
                if ("SUCCESS".equals(reqParams.get("result_code"))
                        && app_id.equals(reqParams.get("appid"))
                        && money.equals(reqParams.get("total_fee"))) {

                    log.info("微信异步通知成功");
                    params.put("return_code", "SUCCESS");
                    params.put("return_msg", "OK");
                } else
                    params.put("return_msg", "参数格式校验错误");
            }
            resp.getWriter().write(XmlUtils.toXml(params));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static Map getOpenId(String code) {
        String requestUrl = get_user_info.replace("APPID", app_id)
                .replace("SECRET", appSecrect)
                .replace("CODE", code);
        JSONObject jsonObject = HttpClientUtil.httpRequest(requestUrl, "GET", null);
        log.info(jsonObject.toString());
        Map result = new HashMap();
        if (null != jsonObject) {
            try {
                Iterator iterator = jsonObject.keys();
                String key = null;
                String value = null;
                while (iterator.hasNext()) {
                    key = (String) iterator.next();
                    value = jsonObject.getString(key);
                    result.put(key, value);
                }
            } catch (Exception e) {
                log.info("微信OpenID获取失败", e);
            }
        }
        return result;

    }

    //=============================================================================//

    public static boolean checkParam(Map<String,String> params, String key){
        boolean result = false;
        if(params.containsKey("sign")){
            String sign = params.get("sign");
            sign(params,key);
            result = sign.equalsIgnoreCase(params.get("sign"));
            log.info("微信验签：" + sign);
            log.info("微信验签：" + params.get("sign"));
        }
        return result;
    }

    public static void sign(Map<String, String> params, String key) {
        String stringA = createLinkString(params);
        String stringSignTemp = stringA + "&key=" + key;
        String sign = MD5.sign(stringSignTemp, Consts.UTF_8.name()).toUpperCase();
        log.info("微信签名 sign：" + stringSignTemp);
        log.info("微信签名 sign：" + sign);
        params.put("sign", sign);
    }

    /**
     * 发送请求，并获取响应map
     *
     * @param url      请求地址
     * @param params   参数map
     * @param isRefund 是否退款
     * @return
     */
    public static Map<String, Object> getResult(HttpServletRequest request, String url,
                                                Map<String, String> params, boolean isRefund, String SSLPath) {
        String xmlData = mapConvert2XmlString(params);
        log.info("请求参数:【" + xmlData + "】");
        Map<String, Object> resultMap = new HashMap<String, Object>();
        try {
            String result;
            if (isRefund)
                result = ClientCustomSSL.doRefund(request, url, xmlData, SSLPath, params.get("mch_id"));
            else
                result = HttpClientUtil.postXml(url, xmlData);
            resultMap = XmlUtils.stringToMap(result);
        } catch (DocumentException e) {
            log.error("解析微信返回xml发生异常，", e);
        } catch (Exception e) {
            log.error("发起微信退款请求发生异常，", e);
        }
        return resultMap;
    }

    /**
     * 根据map构建一个xml格式字符串
     *
     * @param map
     * @return
     */
    public static String mapConvert2XmlString(Map<String, String> map) {
        StringBuilder builder = new StringBuilder();
        builder.append("<xml>");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            builder.append("<").append(entry.getKey()).append(">")
                    .append(entry.getValue()).append("</")
                    .append(entry.getKey()).append(">");
        }
        builder.append("</xml>");
        return builder.toString();
    }

    public static String getNonceStr() {
        Random random = new Random();
        return MD5.sign(String.valueOf(random.nextInt(10000)), Consts.UTF_8.name());
    }

    /**
     * 构建唤起支付的参数
     *
     * @param prepayId 预支付id
     * @return
     */
    public static Map<String, String> buildPay(String prepayId,
                                               Map<String, String> params) {
        Map<String, String> payParams = new HashMap<>();
        payParams.put("appid", WxpayUtil.app_id);
        payParams.put("partnerid", WxpayUtil.mch_id);
        payParams.put("prepayid", prepayId);
        payParams.put("package", "Sign=WXPay");
        payParams.put("noncestr", params.get("nonce_str"));
        payParams.put("timestamp", getTimeStamp());
        return payParams;
    }


    /**
     * 生成时间戳
     *
     * @return
     */
    public static String getTimeStamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }


    public static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (StringUtils.isBlank(value) || key.equalsIgnoreCase("sign") || key.equalsIgnoreCase("sign_type"))
                continue;
            prestr += key + "=" + value + "&";
        }
        if(StringUtils.isNotBlank(prestr))
            prestr = prestr.substring(0,prestr.length() - 1);
        return prestr;
    }

}
